﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Caliburn.Micro;
using System.ComponentModel.Composition;
using EmraldClient.Properties;
using System.Linq;

namespace EmraldClient
{
  [Export(typeof(IShell))]
  public class ShellViewModel : Conductor<MyScreen>, IShell
  {
    private const string DatabaseXml = "database.xml";

    private readonly IEmraldService _connection;
    private readonly IStorageService _storageService;
    private static readonly ILog Log = LogManager.GetLog(typeof(ShellViewModel));

    public override string DisplayName
    {
      get { return "chess.Emrald.net"; }
      set { base.DisplayName = value; }
    }
    [ImportingConstructor]
    public ShellViewModel(
      [ImportMany]IEnumerable<Lazy<IEmraldService, IPluginMetadata>> emraldService,
      [ImportMany]IEnumerable<Lazy<IStorageService, IPluginMetadata>> storageService)
    {
#if STUB
      _connection = emraldService.First(s => s.Metadata.IsDebug).Value;
      _storageService = storageService.First(s => s.Metadata.IsDebug).Value; 
#else
      _connection = emraldService.First(s => !s.Metadata.IsDebug).Value;
      _storageService = storageService.First(s => !s.Metadata.IsDebug).Value; 
#endif
      _storageService.Load(DatabaseXml);
      ShowEnterPassword();
    }

    private void ShowEnterPassword()
    {
      var p = new EnterPasswordViewModel(_connection);
      p.Done += (s, e) => Logon(p.UserName, p.Password);
      ActiveItem = p;
    }

    private void Logon(string userName, string password)
    {
      if (userName != "guest" && password != "")
      {
        Settings.Default.UserName = userName;
        Settings.Default.Password = password;
        Settings.Default.Save();
      }

      var w = new WaitViewModel { Message = "Logging on..." };
      ActiveItem = w;
      var logon = Task<bool>.Factory.StartNew(
        () => _connection.Logon(userName, password));
      logon.ContinueWith(t =>
        {
          if (t.IsFaulted)
          {
            Log.Error(t.Exception);
            var err = new ErrorViewModel(t.Exception);
            err.Done += (s, e) => ShowEnterPassword();
            ActiveItem = err;
          }
          else if (t.Result)
          {
            Log.Info("Logged on as " + userName);
            LoadNextProblem();
          }
          else
          {
            Log.Info("Wrong password!");
            ShowWrongPassword();
          }
        },
        CancellationToken.None,
        TaskContinuationOptions.None,
        TaskScheduler.FromCurrentSynchronizationContext());
    }

    private void ShowWrongPassword()
    {
      var w = new WrongPasswordViewModel();
      w.Done += (s, e) => ShowEnterPassword();
      ActiveItem = w;
    }


    private void SendResults(Problem p)
    {
      Status("Sending results...");
      Task.Factory.StartNew(() => _connection.SendResults(p),
        CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).
        ContinueWith(t => LoadNextProblem(),
        CancellationToken.None,
        TaskContinuationOptions.OnlyOnRanToCompletion,
        TaskScheduler.FromCurrentSynchronizationContext());
    }

    private void Status(string text)
    {
      var g = ActiveItem as GameViewModel;
      if (g != null)
      {
        g.IsLoading = true;
        g.Status = text;
      }
      var w = ActiveItem as WaitViewModel;
      if (w != null)
      {
        w.Message = text;
      }
    }

    private void LoadNextProblem()
    {
      Status("Loading problem...");
      Task<Problem>.Factory.StartNew(GetProblem,
        CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).
        ContinueWith(LoadProblem, CancellationToken.None,
        TaskContinuationOptions.NotOnCanceled,
        TaskScheduler.FromCurrentSynchronizationContext());
    }
    private Problem GetProblem()
    {
      while (true)
      {
        var problem = _connection.GetProblem();
        if (problem == null) return null;
        if (_storageService.Storage.ContainsKey(problem.Id)) continue;
        _storageService.Storage[problem.Id] = problem;
        return problem;
      }
    }
    private void LoadProblem(Task<Problem> t)
    {
      if (t.IsFaulted)
      {
        Log.Error(t.Exception);
        var err = new ErrorViewModel(t.Exception);
        err.Done += (s, e) => ShowEnterPassword();
        ActiveItem = err;
      }
      else if (t.IsCompleted)
      {
        var p = t.Result;
        var game = new GameViewModel(p, _connection.UserName);
        game.Done += (s, e) => SendResults(p);
        ActiveItem = game;
      }

    }

    protected override void OnDeactivate(bool close)
    {
      _connection.Logout();
      _storageService.Save(DatabaseXml);
      base.OnDeactivate(close);
    }
  }
}
